package com.bd.dao;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import com.bd.util.DaoUtil;
import com.bd.util.StringUtil;

public class Dao<T> {

	public T createEntityFromResultSet(ResultSet rs) {
		T t = null;
		try {
			t = getEntityClass().newInstance();

			Method methods[] = t.getClass().getDeclaredMethods();
			for (Method m : methods)
				if (m.getName().contains("set")) {
					String name = m.getName().replace("set", "");
					if (!isCollection(m)) {
						Object obj = rs.getObject(name);

						if (m.toString().contains("String[]"))
							m.invoke(t, new Object[] { new String[2] });
						else
							m.invoke(t, obj);
					}
				}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return t;
	}

	public boolean isCollection(Method m) throws Exception {
		String name = m.getName().replace("set", "");
		name = (name.charAt(0) + "").toLowerCase() + name.substring(1, name.length());

		Field f = getEntityClass().getDeclaredField(name);

		return f.getType().isInstance(new ArrayList<T>());
		// else if (m.getGenericReturnType().toString().contains("List"))
		// return true;
		// return false;
	}

	public T findById(int id) {
		T obj = null;
		String sql = "SELECT * FROM " + getTableName() + " where id = ?";
		try {
			PreparedStatement ps = getConnection().prepareStatement(sql);
			ps.setInt(1, id);

			ResultSet rs = ps.executeQuery();
			if (rs.next())
				obj = createEntityFromResultSet(rs);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return obj;
	}

	public List<T> findByParameter(String[] paramNames, String... param) {
		List<T> lst = new ArrayList<T>();

		try {
			StringBuilder sql = new StringBuilder("SELECT * FROM " + getTableName() + " where ");
			for (int i = 0; i < paramNames.length; i++)
				sql.append(paramNames[i] + "= ? " + (i == paramNames.length - 1 ? "" : "AND "));

			PreparedStatement ps = getConnection().prepareStatement(sql.toString());

			for (int i = 1; i <= param.length; i++)
				ps.setObject(i, param[i - 1]);

			ResultSet rs = ps.executeQuery();

			while (rs.next()) {
				T t = createEntityFromResultSet(rs);
				lst.add(t);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return lst;
	}

	public List<T> findLike(String column, Object value) {
		List<T> lst = new ArrayList<T>();
		try {
			String sql = "SELECT * FROM " + getTableName() + " WHERE " + column + " LIKE "
							+ (value == null || value.toString().isEmpty() ? "'%'" : "'%" + value.toString() + "%'");

			PreparedStatement ps = getConnection().prepareStatement(sql);
			// if (value != null && !value.toString().isEmpty())
			// ps.setObject(1, value);

			ResultSet rs = ps.executeQuery();

			while (rs.next()) {
				T t = createEntityFromResultSet(rs);
				lst.add(t);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return lst;
	}

	public List<T> findAll() {
		List<T> lst = new ArrayList<T>();
		String sql = "SELECT * FROM " + getTableName();

		try {
			PreparedStatement ps = getConnection().prepareStatement(sql);
			ResultSet rs = ps.executeQuery();

			while (rs.next()) {
				T t = createEntityFromResultSet(rs);
				lst.add(t);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return lst;
	}

	public boolean save(T entity) {
		int lastId = 0;
		StringBuilder builder = new StringBuilder();
		try {

			Method method = entity.getClass().getDeclaredMethod("getId", null);
			Integer id = (Integer) method.invoke(entity);

			if (id == null || id.equals(0)) {

				lastId = findLastId();

				builder = new StringBuilder("INSERT INTO " + getTableName() + " (");
				Field f[] = entity.getClass().getDeclaredFields();

				Object obj = null;
				int i = 1;

				int length = f.length;

				for (Method m : entity.getClass().getDeclaredMethods())
					if (m.getName().contains("get"))
						if (!isCollection(m) && i <= length - 1)
							builder.append(m.getName().replace("get", "") + ", ");

				for (Field fs : f)
					if (fs.toString().contains("List"))
						length--;

				int lastPosition = builder.lastIndexOf(",");
				builder.replace(lastPosition, lastPosition + 1, ") VALUES (");

				for (int j = 0; j < length; j++)
					builder.append("? " + (j == length - 1 ? ")" : ","));

				PreparedStatement ps = getConnection().prepareStatement(builder.toString());

				ps.setInt(i++, lastId + 1);

				for (Method m : entity.getClass().getDeclaredMethods())
					if (m.getName().contains("get") && !m.getName().equalsIgnoreCase("getId"))
						if (!isCollection(m) && i <= length) {
							obj = m.invoke(entity);
							ps.setObject(i++, obj);
						}

				id = ps.executeUpdate();

				return id > 0;
			} else
				return update(entity);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	public boolean update(T entity) {
		StringBuilder builder = new StringBuilder("UPDATE " + getTableName() + " SET ");
		boolean update = false;

		try {
			Method[] methods = entity.getClass().getDeclaredMethods();

			for (Method m : methods) {
				String name = m.getName();
				if (name.contains("get") && !isCollection(m) && !name.equalsIgnoreCase("getId"))
					builder.append(name.replace("get", "") + " = ?, ");
			}

			int indexOfComma = builder.lastIndexOf(",");

			builder.replace(indexOfComma, indexOfComma + 1, " WHERE id = ?");

			PreparedStatement ps = getConnection().prepareStatement(builder.toString());

			int i = 1;

			for (Method m : methods) {
				String name = m.getName();

				if (name.contains("get") && !isCollection(m) && !name.equalsIgnoreCase("getId"))
					ps.setObject(i++, m.invoke(entity));
			}

			Method method = entity.getClass().getDeclaredMethod("getId");
			ps.setInt(i, (Integer) method.invoke(entity));

			int id = ps.executeUpdate();

			update = id > 0;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return update;
	}

	public boolean delete(T entity) {
		StringBuilder builder = new StringBuilder("DELETE FROM " + getTableName() + " WHERE id = ?");
		boolean delete = false;
		try {
			PreparedStatement ps = getConnection().prepareStatement(builder.toString());
			Method m = entity.getClass().getDeclaredMethod("getId");

			ps.setObject(1, m.invoke(entity));
			delete = ps.executeUpdate() > 0;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return delete;
	}

	public Integer findLastId() {
		int num = 0;
		String sql = "SELECT max(id) FROM " + getTableName();

		try {
			Statement st = getConnection().createStatement();
			ResultSet rs = st.executeQuery(sql);

			if (rs.next())
				num = rs.getInt(0);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return num;
	}

	public ResultSet executeNativeSQL(String sql) {
		ResultSet rs = null;
		try {
			Statement st = getConnection().createStatement();
			rs = st.executeQuery(sql);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return rs;
	}

	public Connection getConnection() {
		return DaoUtil.getConnectionInstance();
	}

	public Class<T> getEntityClass() {
		return null;
	}

	public String getTableName() {
		return StringUtil.formatClassName(getEntityClass());
	}
}
